package xdu.lz.netty.c_nio_buffer_channel;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;
import java.util.Random;

public class NioBuffer_channel {

    /**
     * 1测试buffer
     */
    @Test
    public void test1(){
        //指定buffer长度
        IntBuffer buffer=IntBuffer.allocate(10);
        //加入随机数
        for (int i=0;i<buffer.capacity();i++){
            int random=new Random().nextInt(20);
            buffer.put(random);
        }
        //将buffer转换一下，读写切换
        buffer.flip();
        //读取,数据会从buffer数组中拿出来   类似出队
        while (buffer.hasRemaining()){
            System.out.println(buffer.get());
        }

    }

    /**
     * 2读取文件内容   ---- channel
     * @throws Exception
     */
    @Test
    public void test2ReadFile() throws Exception{
        FileInputStream fileInputStream = new FileInputStream("NioTest2.txt");
        FileChannel fileChannel = fileInputStream.getChannel();

        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
        fileChannel.read(byteBuffer); //这里是把文件的内容写到buffer是一个写buffer的过程

        byteBuffer.flip();//下面是读buffer所以要翻转一下buffer的状态

        while(byteBuffer.remaining() > 0) {
            byte b = byteBuffer.get();
            System.out.println("Character: " + (char)b);
        }

        fileInputStream.close();
    }

    /**
     * 3向文件写数据  ---- channel
     * @throws Exception
     */
    @Test
    public void test3WriteFile() throws Exception{
        FileOutputStream fileOutputStream = new FileOutputStream("NioTest3.txt");
        FileChannel fileChannel = fileOutputStream.getChannel();

        ByteBuffer byteBuffer = ByteBuffer.allocate(512);

        byte[] messages = "hello world welcome, nihao".getBytes();

        //把数据读到buffer
        for(int i = 0; i < messages.length; ++i) {
            byteBuffer.put(messages[i]);
        }

        byteBuffer.flip();

        //把buffer数据写入到channel
        fileChannel.write(byteBuffer);

        fileOutputStream.close();
    }


    /**
     * 4理解buffer中 capacity limit position
     */
    @Test
    public void test4(){
        //指定buffer长度
        IntBuffer buffer=IntBuffer.allocate(10);
        //加入随机数
        for (int i=0;i<5;i++){
            buffer.put(i);
        }

        //将buffer转换一下，读写切换， limit 的 index= 5
        buffer.flip();
        //读取
        while (buffer.hasRemaining()){
            System.out.println(buffer.get());

        }
        //The position is set to zero and the mark is discarded.
        buffer.rewind();
        System.out.println("**********");
        //和上面结果一样
        while (buffer.hasRemaining()){
            System.out.println(buffer.get());

        }
        System.out.println("1:"+buffer);
        /**
         * he limit is set to the current position and then
 *      * the position is set to zero.  If the mark is defined then it is
 *      * discarded.
         */
        buffer.flip();

        //此时5个元素都被读出来了，所以指针指向第一个位置,但是limit仍然指向index = 5
        for (int i = 0; i < 5 ; i++) {
            System.out.println(i+":"+buffer);
            buffer.put(100);
        }
//        buffer.put(100);  //再次放入数据会出现BufferOverflowException异常
        buffer.flip();
        System.out.println(buffer.get(4));
    }

    /**
     * 5文件读写综合 :读取A文件内容并写入B文件
     * @throws Exception
     */
    @Test
    public void test5ReadWriteFile() throws Exception{
        FileInputStream inputStream = new FileInputStream("input.txt");
        FileOutputStream outputStream = new FileOutputStream("output.txt");
        FileChannel inputChannel = inputStream.getChannel();
        FileChannel outputChannel = outputStream.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate(512);

        while(true) {
            /**1这行代码是让三个参数初始化状态，第二次进入的时候，文件读到了末尾，所以buffer会返回-1
             * 2如果注视掉这行代码，导致第二次进入循环时候
             * position和limit位置一样，这样的话buffer永远不会再去读取数据，
             * 所以read一直返回是0，
             * 接着flip一下，position会值为0，那么会把第一次读到的内容重新写到文件
             * 如此循环下去，read一直是0，每次都会把第一次读到的内容追加到output.txt中
             */
//            buffer.clear(); // 如果注释掉该行代码会发生什么情况？
            int read = inputChannel.read(buffer);
            System.out.println("read: " + read);

            if(-1 == read) {
                break;
            }

            buffer.flip();
            outputChannel.write(buffer);

        }
        inputChannel.close();
        outputChannel.close();
    }

    /**
     * 6 put不止可以存放byte，其他类型也可以，但是字节数不一样，底层最终都是转成byte
     *
     * 注意获取的时候要按照对应的类型获取
     */
    @Test
    public void test6(){
        ByteBuffer buffer = ByteBuffer.allocate(64);

        buffer.putInt(15);
        buffer.putLong(500000000L);
        buffer.putDouble(14.123456);
        buffer.putChar('你');
        buffer.putShort((short)2);
        buffer.putChar('我');

        buffer.flip();

        System.out.println(buffer.getInt());
        System.out.println(buffer.getLong());
        System.out.println(buffer.getDouble());
        System.out.println(buffer.getChar());
        System.out.println(buffer.getShort());
        System.out.println(buffer.getChar());
//        while ( buffer.hasRemaining()){
//            System.out.println(buffer.get());
//        }
    }

    /**
     * 7 buffer切片
     *  buffer的切片  slicebuffer 是buffer的一部分，
     *      两者公用数组buffer，共享一份数据
     *      但是两者又各自拥有各自的capacity，limit，position
     *
     */
    @Test
    public void test7SliceBuffer(){
        ByteBuffer buffer = ByteBuffer.allocate(10);

        for(int i = 0; i < buffer.capacity(); ++i) {
            buffer.put((byte)i);
        }

        buffer.position(2);
        buffer.limit(6); //包头不包尾

        ByteBuffer sliceBuffer = buffer.slice();

        for(int i = 0; i < sliceBuffer.capacity(); ++i) {
            byte b = sliceBuffer.get(i);
            b *= 2;
            sliceBuffer.put(i, b);
        }

        buffer.position(0);
        buffer.limit(buffer.capacity());

        while(buffer.hasRemaining()) {
            System.out.println(buffer.get());
        }
    }

    /**
     * 8 只读buffer
     *   以随时将一个普通Buffer调用asReadOnlyBuffer方法返回一个只读Buffer
     *   但不能将一个只读Buffer转换为读写Buffer
     *   二者共享一份数据，只读buffer的实现是 HeapBufferR，所有的修改方法都直接抛异常
     */
    @Test
    public void test8ReadOnlyBuffer(){
        ByteBuffer buffer = ByteBuffer.allocate(10);

        System.out.println(buffer.getClass());

        for (int i = 0; i < buffer.capacity(); ++i) {
            buffer.put((byte)i);
        }

        ByteBuffer readonlyBuffer = buffer.asReadOnlyBuffer();

        System.out.println(readonlyBuffer.getClass());

        readonlyBuffer.position(0);
//        readonlyBuffer.put((byte)2);
    }
}
